<?php
defined('BASE') or exit('Access Denied!');

/**
 * Obullo Framework (c) 2009.
 *
 * PHP5 MVC Minimalist Software for PHP 5.2.4 or newer
 * 
 *
 * @package         Obullo
 * @author          Obullo.com  
 * @subpackage      Base.database        
 * @copyright       Copyright (c) 2009 Ersin Güvenç.
 * @license         public
 * @since           Version 1.0
 * @filesource
 */ 
 
/**
 * DB Class.
 *
 * Extending PDO Class.
 *
 * @package         Obullo 
 * @subpackage      Base.database     
 * @category        Database
 * @version         0.1
 * @version         0.2 added active record class
 */

//---------- DB Classes load schema ----------//

//  
// include DBAC_Adapter.php
// 
// DBAC_Adapter::AC($active_record = true); 
//      o include DBResults.php
//      o include DBac_record.php
//      o include DBac_sw_on.php

// DBAC_Adapter::AC($active_record = false);
//      o include DBResults.php
//      o include DBac_sw_off.php

// include DB.php
// include DBFactory.php


Class DB extends DBAC_Switch
{
    /**
    * prepare switch
    * 
    * @var boolean
    */
    public $prepare = FALSE;
    
    /**
    * Prepare options
    * 
    * @var mixed
    */
    public $p_opt = array();
    
    /**
    * Store last queried sql
    * 
    * @var string
    */
    public $last_sql = '';
    
    /**
    * Store last PDO execute 
    * values
    * 
    * @var array
    */
    private $last_values;
    
    /**
    * Parent Query - PDOStatement Object
    * 
    * @var object
    */
    private $PQ = '';

    /**
    * Count execute func.
    * 
    * @var int
    */
    public $exec_count = 0;
    
    
    function __construct($dsn, $user = NULL, $pass = NULL, $options = NULL)
    {
        parent::__construct($dsn, $user, $pass, $options);
        
        //throw new DBException('SQLSTATE: test error!');
    } 
    
    // PDO prepare function.
    public function prep($options = array())
    {
        $this->p_opt   = &$options;
        $this->prepare = TRUE;
    }
    
    /**
    * Flexible Prepared or Direct Query
    *         
    * @author  Ersin Güvenç
    * @param   string $sql
    * @version 1.0
    * @return  PDOStatement
    */
    public function query($sql = NULL)
    {   
        $this->last_sql = $sql;
        
        if($this->prepare)
        {
            $this->PQ = parent::prepare($sql,$this->p_opt); 
            return NULL;
        }
        
        $this->PQ = parent::query($sql);

        return $this;
    }                    
        
    // escape like
    private static function escape_like($str)
    {
        $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); 
        return $str;
    }
        
    /**
    * Execute prepared query
    * 
    * @author   Ersin Güvenç
    * @author   you..
    * @version  0.1
    * @version  0.2 added secure like conditions support
    * @param    array $array bindValue or bindParam arrays
    * @param    boolean $bindParam switch Default bindValue 
    * @return   void | NULL 
    */
    public function exec($array = NULL, $bindParam = FALSE)
    { 
        if($this->last_sql != NULL AND $this->exec_count == 0)
        $this->query($this->last_sql);

        // if(class_exists('OB_DB_active_record'))
        
        // this is a security reason, if developer use like operator,
        // add % % operators inside of exec func.
        if(count($this->ar_like) > 0)
        {
            $new_array = $array;
            $array = array();
            foreach($new_array as $key=>$val)
            {                                   
                if(strpos($val,'both|') === 0)
                {
                    $val = substr(self::escape_like($val),5); 
                    $val = '%'."{$val}".'%';
                }
                
                if(strpos($val,'before|') === 0)
                {
                    $val = substr(self::escape_like($val),7);
                    $val = '%'."{$val}"; 
                }
                
                if(strpos($val,'after|') === 0)
                {
                    $val = substr(self::escape_like($val),6);
                    $val = "{$val}".'%';
                }
                
                $array[$key] = $val;
        
            }
        }
        
        //print_r($array); exit;
    
        if(is_array($array))
        {
            if( ! self::isAssoc($array))
            throw new DBException('PDO binds array must be associative !');
            
            switch ($bindParam)
            {
               case TRUE:
                $this->_bindParams($array);
                $this->last_values = $array;
                 break;
                 
               default:
               $this->_bindValues($array);
               $this->last_values = $array;
            }
        }
        
        // if no query builded by active record
        // switch to pdo::statement
        $this->PQ->execute();
        
        // reset prepare variable 
        $this->prepare = FALSE;
        
        ++$this->exec_count; 
        
        return NULL;
    }
    
    /**
    * Exec just for INSERT and 
    * UPDATE operations it returns to
    * number of affected rows after the write
    * operations.
    * 
    * @author   Ersin Güvenç
    * @author   you..
    * @param    string $sql
    * @version  0.1
    * @return   boolean
    */
    private function _exec($sql)
    {
        $this->last_sql = $sql;
        
        return parent::exec($sql);
    }
        
        
    // automatically secure bind values..
    private function _bindValues($array)
    {
        foreach($array as $key=>$val)
        {                                          
            switch (gettype($val))
            {
               case 'string':
               //echo 'string'; 
                 $this->bval($key, $val, PDO::PARAM_STR);
                 break;
                 
               case 'integer':
                 $this->bval($key, $val, PDO::PARAM_INT);
                 break;
                 
               case 'boolean':
               //echo 'BOOL';
                 $this->bval($key, $val, PDO::PARAM_BOOL);
                 break;
               
               case 'null':
                 $this->bval($key, $val, PDO::PARAM_NULL);
                 break;
                 
               default:
                 $this->bval($key, $val, PDO::PARAM_STR);
            }
        }
    }

    // automatically secure bind params..
    private function _bindParams($array)
    {
        foreach($array as $key=>$val)
        {                                          
            switch (gettype($val))              
            {
               case 'string':
                 $this->bparam($key, $val, PDO::PARAM_STR);
                 break;
                 
               case 'integer':
                 $this->bparam($key, $val, PDO::PARAM_INT);
                 break;
                 
               case 'boolean':
                 $this->bparam($key, $val, PDO::PARAM_BOOL);
                 break;
               
               case 'null':
                 $this->bparam($key, $val, PDO::PARAM_NULL);
                 break;
                 
               default:
                 $this->bparam($key, $val, PDO::PARAM_STR);
            }
        }
    }
        
    
    /**                               
    * Fetch prepared or none prepared last_query
    * 
    * @author  Ersin Güvenç
    * @version 0.1
    * @version 0.2 added prepared param
    * @param   boolean $prepared
    * @return  string
    */
    public function last_query($prepared = FALSE)
    {
        if($prepared)
        {                                                         
            $vals = $this->last_values;
            
            $quote_added_vals = array();
            foreach(array_values($vals) as $q)
            $quote_added_vals[] = "'".$q."'"; 
        
            return str_replace(array_keys($vals),$quote_added_vals,$this->last_sql);
        }
            
        return $this->last_sql;
    }                 
    
    
    // get PDO::lastInsertId()
    public function insert_id()
    {
        return parent::lastInsertId();
    }
        
    // Alias of PDO_Statement::bindValue()
    public function bval($param, $val, $type)
    {
        $this->PQ->bindValue($param, $val, DBResults::type($type));  
    }
    
    // Alias of PDO_Statement::bindParam()
    public function bparam($param, $val, $type, $length = NULL, $driver_options = NULL)
    {
        $this->PQ->bindParam($param, $val, DBResults::type($type), $length, $driver_options);  
    }        
        
    // Get available drivers on your host
    public function drivers()
    {
        return PDO::getAvailableDrivers();
    }
    
    // Get results as associative array
    public function assoc()
    {
        return $this->PQ->fetch(PDO::FETCH_ASSOC);
    }
    
    // Get result as object
    public function obj()
    {                                  
        return $this->PQ->fetch(PDO::FETCH_OBJ);
    }
    
    // Same as object
    public function row()
    {                                  
        return $this->PQ->fetch(PDO::FETCH_OBJ);  
    }
    
    // Get all results by assoc, object or what u want
    public function all($type = NULL)
    {    
        return $this->PQ->fetchAll(DBResults::fetch($type));
    } 
    
    // Get column numbers, results in assoc
    public function num()
    {    
        return $this->PQ->fetch(PDO::FETCH_NUM);
    } 
    
    // Number of rows
    public function num_rows()
    {    
        return $this->PQ->rowCount();
    }     
    
    // Get column names and numbers (both)
    public function both()
    {
        return $this->PQ->fetch(PDO::FETCH_BOTH);
    } 
    
    // check array associative or not 
    private static function isAssoc($arr)
    {
        return array_keys($arr) !== range(0, count($arr) - 1);
    }

 
} //end class.
 
?>
